home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / CUGUK / COMMS / C011.ZIP / SEALINK.C < prev    next >
Text File  |  1990-01-19  |  21KB  |  555 lines

  1. /********************************************************************
  2.  * C Users Group (U.K) C Source Code Library File CUGLIB.011        *
  3.  * Inquiries to: M. Houston, 36 Whetstone Clo. Farquhar Rd.         *
  4.  * Edgbaston, Birmingham B15 2QN ENGLAND                *
  5.  ********************************************************************
  6.  * File name: sealink.c
  7.  * Program name: library module
  8.  * Source of file: West Midlands Opus
  9.  * Purpose: The sealink file transfer protocol.
  10.  * Changes: <who what when & why major changes have been made>      
  11.  ********************************************************************/
  12.  
  13. /*  SEAlink - Sliding window file transfer protocol
  14.  
  15.     Version 1.00, created on 09/04/86 at 14:37:58
  16.  
  17. (C) COPYRIGHT 1986 by System Enhancement Associates; ALL RIGHTS RESERVED
  18.  
  19.     By:  Thom Henderson
  20.  
  21.     Description:
  22.  
  23.          This file contains a set of routines to illustrate the SEAlink
  24.          sliding window file transfer protocol.  SEAlink is fully backward
  25.          compatible to XMODEM, and can be easily adapted to most XMODEM
  26.          variants.
  27.  
  28.          The intent of SEAlink is to provide a file transfer protocol that
  29.          does not suffer from propagation delays, such as are introduced
  30.          by satellite relays or packet switched networks.
  31.  
  32.     Instructions:
  33.  
  34.          Two routines are provided to implement SEAlink file transfers.
  35.  
  36.          int xmtfile(name)             /+ transmit a file +/
  37.          char *name;                   /+ name of file to transmit +/
  38.  
  39.          This routine is used to send a file.  One file is sent at a time.
  40.          If the name is blank (name is null or *name points to a null),
  41.          then only an end of transmission marker is sent.
  42.  
  43.          This routine returns a one if the file is successfully
  44.          transmitted, or a zero if a fatal error occurs.
  45.  
  46.          char *rcvfile(name)           /+ receive a file +/
  47.          char *name;                   /+ name of file (optional) +/
  48.  
  49.          This routine is used to receive a file.  One file is received.
  50.          The name, if given, takes precedence and will be the name of
  51.          the resulting file.  If the name is blank (name is null or *name
  52.          points to a null), then the name given by the transmitter is used.
  53.          If the transmitter does not give a name, then a default name is
  54.          used.
  55.  
  56.          This routine returns a pointer to the name of the file that
  57.          was received.  If the file transfer is not successful, then
  58.          a null pointer is returned.
  59.  
  60.          The pointer returned by rcvfile() points to a static data buffer.
  61.          This does not have to be freed (and should not be), but it will
  62.          be overwritten the next time rcvfile() is called.
  63.  
  64.          The rcvfile() function works on a temporary file whose name is
  65.          the same as the final file, but with a dash ("-") added at the
  66.          beginning.  If a file transfer is aborted, then this temporary
  67.          file will be retained.  An aborted file transfer will not harm
  68.          a pre-existing file of the same name.
  69.  
  70.     Programming notes:
  71.  
  72.          These routines can be used for either single or multiple file
  73.          transfers.
  74.  
  75.          To send multiple files, send each one one at a time until either
  76.          a transmit fails or all files are sent.  If all files are sent,
  77.          then signal the end by calling xmtfile() with a null pointer.
  78.  
  79.          To receive multiple files, call rcvfile() repeatedly until it
  80.          returns a null pointer.
  81.  
  82.          These routines pass a "block zero", which contains information
  83.          about the original file name, size, and date/time of last
  84.          modification.  If you cannot implement block zero, then you can
  85.          leave it out.  If you cannot set any given field in block zero
  86.          when transmitting, then you should leave it set to zeros.  If you
  87.          cannot use any given field of block zero when receiving, then
  88.          you should ignore it.
  89.  
  90.          These routines are fully compatible with XMODEM, including the
  91.          original checksum method and later CRC adaptations.  It can be
  92.          easily adapted to Modem7 protocol by adding a Modem7 filename
  93.          transfer shell, though we do not recommend it.  The underlying
  94.          logic, of course, can be adapted to almost any variant of XMODEM.
  95.  
  96.     License:
  97.  
  98.          You are granted a license to use this code in your programs, and
  99.          to adapt it to your particular situation and needs, subject only
  100.          to the following conditions:
  101.  
  102.          1)   You must refer to it as the SEAlink protocol, and you must
  103.               give credit to System Enhancement Associates.
  104.  
  105.          2)   If you modify it in such a way that your version cannot
  106.               converse with the original code as supplied by us, then
  107.               you should refer to it as "SEAlink derived", or as a
  108.               "variation of SEAlink", or words to that effect.
  109.  
  110.          In short, we're not asking for any money, but we'd like to
  111.          get some credit for our work.
  112.  
  113.     Language:
  114.          Computer Innovations Optimizing C86
  115. */
  116. #include <stdio.h>
  117. #include <time.h>
  118.  
  119. struct zeros                           /* block zero data structure */
  120. {   long flen;                         /* file length */
  121.     long fstamp;                       /* file date/time stamp */
  122.     char fnam[17];                     /* original file name */
  123.     char prog[15];                     /* sending program name */
  124.     char fill[88];                     /* reserved for future use */
  125. }   ;
  126.  
  127. #define ACK 0x06
  128. #define NAK 0x15
  129. #define SOH 0x01
  130. #define EOT 0x04
  131.  
  132. static int outblk;                     /* number of next block to send */
  133. static int ackblk;                     /* number of last block ACKed */
  134. static int blksnt;                     /* number of last block sent */
  135. static int slide;                      /* true if sliding window */
  136. static int ackst;                      /* ACK/NAK state */
  137. static int numnak;                     /* number of sequential NAKs */
  138. static int chktec;                     /* check type, 1=CRC, 0=checksum */
  139.  
  140. char *progname = "Your name here";     /* name of sending program */
  141.  
  142. /*  File transmitter logic */
  143.  
  144. int xmtfile(name)                      /* transmit a file */
  145. char *name;                            /* name of file to send */
  146. {
  147.     FILE *f, *fopen();                 /* file to send */
  148.     long t1, timerset();               /* timers */
  149.     int c;                             /* one byte of data */
  150.     int endblk;                        /* block number of EOT */
  151.     struct filstat fst;                /* data about file */
  152.     struct zeros zero;                 /* block zero data */
  153.  
  154.     if(name && *name)                  /* if sending a file */
  155.     {    if(!(f=fopen(name,"rb")))
  156.          {    printf("Can't read %s\n",name);
  157.               return 0;
  158.          }
  159.  
  160.          setmem(&zero,sizeof(zero),0); /* clear out data block */
  161.  
  162.          filestat(name,&fst);          /* get file statistics */
  163.          zero.flen = fst.fs_fsize;
  164.          zero.fstamp = fst.fs_abs;
  165.          strcpy(zero.fnam,fst.fs_fname);
  166.          strcpy(zero.prog,progname);
  167.  
  168.          endblk = ((zero.flen+127)/128) + 1;
  169.          printf("Ready to send %d blocks of %s\n",endblk-1,zero.fnam);
  170.     }
  171.     else endblk = 0;                   /* fake for no file */
  172.  
  173.     outblk = 1;                        /* set starting state */
  174.     ackblk = -1;
  175.     blksnt = slide = ackst = numnak = 0;
  176.     chktec = 2;                        /* undetermined */
  177.  
  178.     t1 = timerset(300);                /* time limit for first block */
  179.  
  180.     while(ackblk<endblk)               /* while not all there yet */
  181.     {    if(!carrier())
  182.          {    printf("\nLost carrier\n");
  183.               goto abort;
  184.          }
  185.          if(key_scan()!=EOF)
  186.          {    if((key_getc()&0xff)==27)
  187.               {    printf("\nAborted by operator\n");
  188.                    goto abort;
  189.               }
  190.          }
  191.          if(timeup(t1))
  192.          {    printf("\nFatal timeout\n");
  193.               goto abort;
  194.          }
  195.          if(outblk <= ackblk + (slide? 4 : 1))
  196.          {    if(outblk<endblk)
  197.               {    if(outblk>0)
  198.                         sendblk(f,outblk);
  199.                    else shipblk(&zero,0);
  200.                    printf("Sent block #%d  \r",outblk);
  201.               }
  202.               else if(outblk==endblk)
  203.               {    com_putc(EOT);
  204.                    printf("Sent EOT          \r");
  205.               }
  206.               outblk++;
  207.               t1 = timerset(100);      /* time limit between blocks */
  208.          }
  209.  
  210.          ackchk();
  211.          if(numnak>10)
  212.          {    printf("\nToo many errors\n");
  213.               goto abort;
  214.          }
  215.     }
  216.  
  217.     printf("End of file       \n");
  218.     if(endblk)
  219.          fclose(f);
  220.     return 1;                          /* exit with good status */
  221.  
  222. abort:
  223.     if(endblk)
  224.          fclose(f);
  225.     return 0;                          /* exit with bad status */
  226. }
  227.  
  228. /*  The various ACK/NAK states are:
  229.     0:   Ground state, ACK or NAK expected.
  230.     1:   ACK received
  231.     2:   NAK received
  232.     3:   ACK, block# received
  233.     4:   NAK, block# received
  234.     5:   Returning to ground state
  235. */
  236.  
  237. static ackchk()                        /* check for ACK or NAK */
  238. {
  239.     int c;                             /* one byte of data */
  240.     static int rawblk;                 /* raw block number */
  241.  
  242.     while((c=com_getc(0))!=EOF)
  243.     {    if(ackst==3 || ackst==4)
  244.          {    slide = 0;               /* assume this will fail */
  245.               if(rawblk == c^0xff)     /* see if we believe the number */
  246.               {    rawblk = outblk - ((outblk-rawblk)&0xff);
  247.                    if(rawblk<=outblk && rawblk>outblk-20)
  248.                    {    slide = 1;     /* we have sliding window! */
  249.                         if(ackst==3)
  250.                              ackblk = ackblk>rawblk? ackblk : rawblk;
  251.                         else
  252.                         {    outblk = rawblk<0? 0 : rawblk;
  253.                              com_dump();    /* purge pending output */
  254.                         }
  255.                         printf("\r%s %d == ",ackst==3? "ACK":"NAK",rawblk);
  256.                    }
  257.               }
  258.               ackst = 5;               /* return to ground state */
  259.          }
  260.  
  261.          if(ackst==1 || ackst==2)
  262.          {    rawblk = c;
  263.               ackst += 2;
  264.          }
  265.  
  266.          if(!slide || ackst==0)
  267.          {    if(c==ACK)
  268.               {    if(!slide)
  269.                    {    ackblk++;
  270.                         printf("\rACK %d -- ",ackblk);
  271.                    }
  272.                    ackst = 1;
  273.                    numnak = 0;
  274.               }
  275.  
  276.               else if(c=='C' || c==NAK)
  277.               {    if(chktec>1)        /* if method not determined yet */
  278.                         chktec = (c=='C');  /* then do what rcver wants */
  279.  
  280.                    if(!slide)
  281.                    {    outblk = ackblk+1;
  282.                         printf("\rNAK %d -- ",ackblk+1);
  283.                    }
  284.                    ackst = 2;
  285.                    numnak++;
  286.               }
  287.          }
  288.  
  289.          if(ackst==5)
  290.               ackst = 0;
  291.     }
  292. }
  293.  
  294. static sendblk(f,blknum)               /* send one block */
  295. FILE *f;                               /* file to read from */
  296. int blknum;                            /* block to send */
  297. {
  298.     long blkloc;                       /* address of start of block */
  299.     char buf[128];                     /* one block of data */
  300.  
  301.     if(blknum != blksnt+1)             /* if jumping */
  302.     {    blkloc = (long)(blknum-1) * 128L;
  303.          fseek(f,blkloc,0);            /* move where to */
  304.     }
  305.     blksnt = blknum;
  306.  
  307.     setmem(buf,128,26);                /* fill buffer with control Zs */
  308.     fread(buf,1,128,f);                /* read in some data */
  309.     shipblk(buf,blknum);               /* pump it out the comm port */
  310. }
  311.  
  312. static int shipblk(blk,blknum)         /* physically ship a block */
  313. char *blk;                             /* data to be shipped */
  314. int blknum;                            /* number of block */
  315. {
  316.     char *b = blk;                     /* data pointer */
  317.     int crc = 0;                       /* CRC check value */
  318.     int n;                             /* index */
  319.  
  320.     com_putc(SOH);                     /* block header */
  321.     com_putc(blknum);                  /* block number */
  322.     com_putc(blknum^0xff);             /* block number check value */
  323.  
  324.     for(n=0; n<128; n++)               /* ship the data */
  325.     {    if(chktec)
  326.               crc = crc_update(crc,*b);
  327.          else crc += *b;
  328.          com_putc(*b++);
  329.     }
  330.  
  331.     if(chktec)                         /* send proper check value */
  332.     {    crc = crc_finish(crc);
  333.          com_putc(crc>>8);
  334.          com_putc(crc&0xff);
  335.     }
  336.     else com_putc(crc);
  337.  
  338.     return 1;
  339. }
  340.  
  341. /*  File receiver logic */
  342.  
  343. char *rcvfile(name)                    /* receive file */
  344. char *name;                            /* name of file */
  345. {
  346.     int c;                             /* received character */
  347.     int tries;                         /* retry counter */
  348.     long t1, timerset();               /* timer */
  349.     int blknum;                        /* desired block number */
  350.     int inblk;                         /* this block number */
  351.     FILE *f, *fopen();                 /* file, opener */
  352.     char buf[128];                     /* data buffer */
  353.     char tmpname[100];                 /* name of temporary file */
  354.     static char outname[100];          /* name of final file */
  355.     struct zeros zero;                 /* file header data storage */
  356.     int endblk;                        /* block number of EOT, if known */
  357.     long left;                         /* bytes left to output */
  358.     int n;                             /* index */
  359.     char *stat = "Init";               /* receive block status */
  360.  
  361.     if(name && *name)                  /* figure out a name to use */
  362.          strcpy(outname,name);
  363.     else strcpy(outname,"UNKNOWN.$$$");
  364.     strcpy(tmpname,"-"); strcat(tmpname,outname);
  365.  
  366.     if(!(f=fopen(tmpname,"wb")))       /* open output file */
  367.     {    printf("Cannot create %s",tmpname);
  368.          return NULL;
  369.     }
  370.  
  371.     blknum = 1;                        /* start with block #1 */
  372.     tries = -10;                       /* kludge for first time around */
  373.     chktec = 1;                        /* try for CRC error checking */
  374.     endblk = 0;                        /* we don't know the size yet */
  375.     setmem(&zero,sizeof(zero),0);      /* or much of anything else */
  376.  
  377. nakblock:                              /* we got a bad block */
  378.     printf("NAK block %d %-4s\r",blknum,stat);
  379.     if(++tries>10)
  380.     {    printf("\nToo many errors\n");
  381.          goto abort;
  382.     }
  383.     if(tries==0)                       /* if CRC isn't going */
  384.          chktec = 0;                   /* then give checksum a try */
  385.  
  386.     sendack(0,blknum);                 /* send the NAK */
  387.     goto nextblock;
  388.  
  389. ackblock:                              /* we got a good block */
  390.     printf("ACK block %d %-4s\r",blknum-1,stat);
  391.  
  392. nextblock:                             /* start of "get a block" */
  393.     stat = "";
  394.     if(!carrier())
  395.     {    printf("\nLost carrier\n");
  396.          goto abort;
  397.     }
  398.     if(key_scan()!=EOF)
  399.     {    if((key_getc()&0xff)==27)
  400.          {    printf("\nAborted by operator\n");
  401.               goto abort;
  402.          }
  403.     }
  404.     t1 = timerset(50);                 /* timer to start of block */
  405.     while(!timeup(t1))
  406.     {    c = com_getc(0);
  407.          if(c==EOT)
  408.          {    if(!endblk || endblk==blknum)
  409.                    goto endrcv;
  410.          }
  411.          else if(c==SOH)
  412.          {    inblk = com_getc(5);
  413.               if(com_getc(5) == (inblk^0xff))
  414.                    goto blockstart;    /* we found a start */
  415.          }
  416.     }
  417.     stat = "Time";
  418.     goto nakblock;
  419.  
  420. blockstart:                            /* start of block detected */
  421.     c = blknum&0xff;
  422.     if(inblk==c)                       /* if this is the one we want */
  423.     {    if(getblock(buf))             /* else if we get it okay */
  424.          {    sendack(1,inblk);        /* ack the data */
  425.               for(n=0; n<128; n++)
  426.               {    if(endblk)          /* limit file size if known */
  427.                    {    if(!left)
  428.                              break;
  429.                         left--;
  430.                    }
  431.                    if(fputc(buf[n],f)==EOF)
  432.                    {    printf("\nWrite error (disk full?)\n");
  433.                         goto abort;
  434.                    }
  435.               }
  436.               tries = 0;               /* reset try count */
  437.               blknum++;                /* we want the next block */
  438.               goto ackblock;
  439.          }
  440.          else
  441.          {    stat = chktec? "CRC" : "Chk";
  442.               goto nakblock;           /* ask for a resend */
  443.          }
  444.     }
  445.     else if(inblk==0 && blknum==1)     /* else if this is the header */
  446.     {    if(getblock(&zero))
  447.          {    sendack(1,inblk);        /* ack the header */
  448.               if(!*name)               /* given name takes precedence */
  449.                    strcpy(outname,zero.fnam);
  450.               if(left=zero.flen)       /* length to transfer */
  451.                    endblk = (left+127)/128 + 1;
  452.  
  453.               printf("Receiving");
  454.               if(endblk)
  455.                    printf(" %d blocks of",endblk-1);
  456.               printf(" %s",outname);
  457.               if(*zero.prog)
  458.                    printf(" from %s",zero.prog);
  459.               printf("\n");
  460.               goto ackblock;
  461.          }
  462.          else
  463.          {    stat = chktec? "CRC" : "Chk";
  464.               goto nakblock;           /* bad header block */
  465.          }
  466.     }
  467.     else if(inblk<c || inblk>c+100)    /* else if resending what we have */
  468.     {    getblock(buf);                /* ignore it */
  469.          sendack(1,inblk);             /* but ack it */
  470.          stat = "Dup";
  471.          goto ackblock;
  472.     }
  473.     else                               /* else if running ahead */
  474.     {    printf("Future block     \r");
  475.          goto nextblock;
  476.     }
  477.  
  478. endrcv:
  479.     sendack(0,blknum);
  480.     printf("NAK EOT         \r");
  481.     if(com_getc(20)!=EOT)
  482.          goto nakblock;
  483.     sendack(1,blknum);
  484.     printf("ACK EOT\r");
  485.  
  486.     if(blknum>1)                       /* if we really got anything */
  487.     {    if(zero.fstamp)               /* set stamp, if known */
  488.               setstamp(f,zero.fstamp);
  489.          fclose(f);
  490.          unlink(outname);              /* rename temp to proper name */
  491.          rename(tmpname,outname);
  492.          return outname;               /* signal what file we got */
  493.     }
  494.     else                               /* else no real file */
  495.     {    fclose(f);
  496.          unlink(tmpname);              /* discard empty file */
  497.          return NULL;                  /* signal end of transfer */
  498.     }
  499.  
  500. abort:
  501.     fclose(f);
  502.     return NULL;
  503. }
  504.  
  505. static sendack(acknak,blknum)          /* send an ACK or a NAK */
  506. int acknak;                            /* 1=ACH, 0=NAK */
  507. int blknum;                            /* block number */
  508. {
  509.     if(acknak)                         /* send the right signal */
  510.          com_putc(ACK);
  511.     else if(chktec)
  512.          com_putc('C');
  513.     else com_putc(NAK);
  514.  
  515.     com_putc(blknum);                  /* block number */
  516.     com_putc(blknum^0xff);             /* block number check */
  517. }
  518.  
  519. static int getblock(buf)               /* read a block of data */
  520. char *buf;                             /* data buffer */
  521. {
  522.     int ourcrc = 0, hiscrc;            /* CRC check values */
  523.     int c;                             /* one byte of data */
  524.     int n;                             /* index */
  525.  
  526.     for(n=0; n<128; n++)
  527.     {    if((c=com_getc(5))==EOF)
  528.          {    printf("Short block     \r");
  529.               return 0;
  530.          }
  531.          if(chktec)
  532.               ourcrc = crc_update(ourcrc,c);
  533.          else ourcrc += c;
  534.          *buf++ = c;
  535.     }
  536.  
  537.     if(chktec)
  538.     {    ourcrc = crc_finish(ourcrc);
  539.          hiscrc = (com_getc(5)<<8) | com_getc(5);
  540.     }
  541.     else
  542.     {    ourcrc &= 0xff;
  543.          hiscrc = com_getc(5) & 0xff;
  544.     }
  545.  
  546.     if(ourcrc == hiscrc)
  547.          return 1;
  548.     else
  549.     {    if(chktec)
  550.               printf("Bad CRC         \r");
  551.          else printf("Bad Checksum    \r");
  552.          return 0;
  553.     }
  554. }
  555.